package conf

import (
	"errors"
	"github.com/BurntSushi/toml"
	"github.com/aliyun/alibabacloud-kms-agent/internal/model"
	"strings"
)

var (
	DefaultHttpPort              = 2025
	DefaultDisableSSRFToken      = false
	DefaultSSRFHeaders           = []string{"X-KMS-Token", "X-Vault-Token"}
	DefaultSSRFEnvVariables      = []string{"KMS_TOKEN", "KMS_SESSION_TOKEN", "KMS_CONTAINER_AUTHORIZATION_TOKEN"}
	DefaultPathPrefix            = "/v1/"
	DefaultMaxConnections        = 800
	DefaultResponseType          = 0
	DefaultIgnoreTransientErrors = true

	DefaultRegion     = "cn-hangzhou"
	DefaultEndpoint   = ""
	DefaultCaPathFile = ""

	DefaultCache      = "InMemory"
	DefaultTtlSeconds = 300
	DefaultCacheSize  = 1000
	DefaultEnableLRU  = false

	DefaultLogLevel   = "Debug"
	DefaultLogPath    = "./logs/"
	DefaultLogSize    = 200 // MB
	DefaultLogBackups = 3

	InvalidConfigErrMsg         = "invalid config error"
	InvalidHttpPortErrMsg       = "invalid HTTP port"
	InvalidTtlSecondsErrMsg     = "invalid TTL seconds"
	InvalidCacheSizeErrMsg      = "invalid cache size"
	InvalidMaxConnectionErrMsg  = "invalid max connections"
	InvalidPathPrefixErrMsg     = "invalid prefix"
	EmptySSRFHeadersErrMsg      = "empty ssrf headers"
	EmptySSRFEnvVariablesErrMsg = "empty ssrf envs"
	InvalidResponseType         = "invalid response type"
)

type Config struct {
	Server ServerConfig `toml:"Server"`
	Kms    KmsConfig    `toml:"Kms"`
	Cache  CacheConfig  `toml:"Cache"`
	Log    LogConfig    `toml:"Log"`
}

type ServerConfig struct {
	HttpPort              *int      `toml:"HttpPort,omitempty"`
	DisableSSRFToken      *bool     `toml:"DisableSSRFToken,omitempty"`
	SSRFHeaders           *[]string `toml:"SSRFHeaders,omitempty"`
	SSRFEnvVariables      *[]string `toml:"SSRFEnvVariables,omitempty"`
	PathPrefix            *string   `toml:"PathPrefix,omitempty"`
	MaxConn               *int      `toml:"MaxConn,omitempty"`
	ResponseType          *int      `toml:"ResponseType,omitempty"`
	IgnoreTransientErrors *bool     `toml:"IgnoreTransientErrors,omitempty"`
}
type KmsConfig struct {
	Region     *string `toml:"Region,omitempty"`
	Endpoint   *string `toml:"Endpoint,omitempty"`
	CaFilePath *string `toml:"CaFilePath,omitempty"`
}

type CacheConfig struct {
	CacheType  *string `toml:"CacheType,omitempty"`
	TtlSeconds *int    `toml:"TtlSeconds,omitempty"`
	CacheSize  *int    `toml:"CacheSize,omitempty"`
	EnableLRU  *bool   `toml:"EnableLRU,omitempty"`
}

type LogConfig struct {
	LogLevel   *string `toml:"LogLevel,omitempty"`
	LogPath    *string `toml:"LogPath,omitempty"`
	MaxSize    *int    `toml:"MaxSize,omitempty"`
	MaxBackups *int    `toml:"MaxBackups,omitempty"`
}

func LoadConfigFormFile(configPath string) (Config, error) {
	var cfg = Config{}
	_, err := toml.DecodeFile(configPath, &cfg)
	if err != nil {
		return Config{}, errors.New(InvalidConfigErrMsg)
	}

	setDefaults(&cfg)

	err = validateConfig(cfg)
	if err != nil {
		return Config{}, err
	}
	return cfg, nil
}

func DefaultConfig() Config {
	c := Config{}
	setDefaults(&c)
	return c
}

func setDefaults(c *Config) {
	// server
	if c.Server.HttpPort == nil {
		c.Server.HttpPort = &DefaultHttpPort
	}
	if c.Server.DisableSSRFToken == nil {
		c.Server.DisableSSRFToken = &DefaultDisableSSRFToken
	}
	if c.Server.SSRFHeaders == nil {
		c.Server.SSRFHeaders = &DefaultSSRFHeaders
	}
	if c.Server.SSRFEnvVariables == nil {
		c.Server.SSRFEnvVariables = &DefaultSSRFEnvVariables
	}
	if c.Server.PathPrefix == nil {
		c.Server.PathPrefix = &DefaultPathPrefix
	}
	if c.Server.MaxConn == nil {
		c.Server.MaxConn = &DefaultMaxConnections
	}
	if c.Server.ResponseType == nil {
		c.Server.ResponseType = &DefaultResponseType
	}
	if c.Server.IgnoreTransientErrors == nil {
		c.Server.IgnoreTransientErrors = &DefaultIgnoreTransientErrors
	}

	// kms
	if c.Kms.Region == nil {
		c.Kms.Region = &DefaultRegion
	}
	if c.Kms.Endpoint == nil {
		c.Kms.Endpoint = &DefaultEndpoint
	}
	if c.Kms.CaFilePath == nil {
		c.Kms.CaFilePath = &DefaultCaPathFile
	}

	// cache
	if c.Cache.CacheType == nil {
		c.Cache.CacheType = &DefaultCache
	}
	if c.Cache.TtlSeconds == nil || *c.Cache.TtlSeconds == 0 {
		c.Cache.TtlSeconds = &DefaultTtlSeconds
	}
	if c.Cache.CacheSize == nil {
		c.Cache.CacheSize = &DefaultCacheSize
	}
	if c.Cache.EnableLRU == nil {
		c.Cache.EnableLRU = &DefaultEnableLRU
	}

	// log
	if c.Log.LogLevel == nil {
		c.Log.LogLevel = &DefaultLogLevel
	}
	if c.Log.LogPath == nil {
		c.Log.LogPath = &DefaultLogPath
	}
	if c.Log.MaxSize == nil {
		c.Log.MaxSize = &DefaultLogSize
	}
	if c.Log.MaxBackups == nil {
		c.Log.MaxBackups = &DefaultLogBackups
	}
}

func validateConfig(c Config) (err error) {
	// server
	if *c.Server.HttpPort <= 0 || *c.Server.HttpPort > 65535 {
		return errors.New(InvalidHttpPortErrMsg)
	}

	if !*c.Server.DisableSSRFToken {
		if len(*c.Server.SSRFHeaders) == 0 {
			return errors.New(EmptySSRFHeadersErrMsg)
		}
		if len(*c.Server.SSRFEnvVariables) == 0 {
			return errors.New(EmptySSRFEnvVariablesErrMsg)
		}
	}

	if !strings.HasPrefix(*c.Server.PathPrefix, "/") {
		return errors.New(InvalidPathPrefixErrMsg)
	}

	if *c.Server.MaxConn <= 0 || *c.Server.MaxConn > 1000 {
		return errors.New(InvalidMaxConnectionErrMsg)
	}

	if model.ResponseType(*c.Server.ResponseType) != model.ResponseTypeForAliyunKMS &&
		model.ResponseType(*c.Server.ResponseType) != model.ResponseTypeForAWSSecretManager &&
		model.ResponseType(*c.Server.ResponseType) != model.ResponseTypeForVaultKvSecret {
		return errors.New(InvalidResponseType)
	}

	// cache
	if *c.Cache.TtlSeconds < 0 {
		return errors.New(InvalidTtlSecondsErrMsg)
	}

	if *c.Cache.CacheSize < 0 || *c.Cache.CacheSize > 1000 {
		return errors.New(InvalidCacheSizeErrMsg)
	}
	return nil
}
